App.require(["htmlTool"], function (htmlTool) {
    var app = App.getCurrent();

    app.defineComponent("str-tree", {
        render: function (next) {
            this.computedData();
            this.fetchAll(["./li.html", "./index.html"], function (textList) {
                this.data.liHTML = textList[0];
                next(this.renderHTML(textList[1].replace("{{liHTML}}", textList[0])));
            });
        },
        get indeterminate() {
            var childs = this.domList.strTree.children, allChecked = true, allNotChecked = true;
            for (var i = 0; i < childs.length; i++) {
                var components = this.getComponentsByInDom(childs[i]);
                var checked = components[0].checked;
                allChecked = allChecked && checked;
                allNotChecked = allNotChecked && !checked;
            }
            if (!allChecked && !allNotChecked) return true;
            return false;
        },
        set indeterminate(val) {
            if (this.property.haschecked) {
                this.domList.treeToggle.indeterminate = val;
                if (val) {
                    this.parent.indeterminate = true;
                }
            }
        },
        set checked(val) {
            var treeToggle = this.domList.treeToggle;
            var childs = this.domList.strTree.children;
            for (var i = 0; i < childs.length; i++) {
                var components = this.getComponentsByInDom(childs[i]);
                if (!components[0].disabled) components[0].checked = val;
            }
            if (this.property.haschecked) {
                treeToggle.indeterminate = false;
                var state = this.getState();
                switch (state) {
                    case "indeterminate": treeToggle.indeterminate = true; break;
                    case "checked": treeToggle.checked = true; break;
                    case "notchecked": treeToggle.checked = false; break;
                }
            }
        },
        get checked() {
            var childs = this.domList.strTree.children;
            for (var i = 0; i < childs.length; i++) {
                var components = this.getComponentsByInDom(childs[i]);
                if (!components[0].checked) return false;
            }
            return true;
        },
        getCheckedData: function () {
            var result = [], childs = this.domList.strTree.children;
            for (var i = 0; i < childs.length; i++) {
                var component = this.getComponentsByInDom(childs[i])[0];
                if (component.tagName == "str-toggle" && component.checked) result.push(this.data.nodes[i]);
                else if (component.tagName == "str-tree") {
                    var data = component.getCheckedData();
                    for (var j = 0; j < data.length; j++) {
                        result.push(data[j]);
                    }
                }
            }
            return result;
        },
        setInitData: function (list) {
            for (var i = 0; i < list.length; i++) {
                var item = list[i];
                var obj = this._getTreeObjByValue(item.value);
                obj.component.checked = true;
                obj.target._changeValue(obj.component);
            }
        },
        reset: function () {
            var nodes = this.data.nodes, childs = this.domList.strTree.children;
            for (var i = 0; i < nodes.length; i++) {
                var node = nodes[i], component = this.getComponentsByInDom(childs[i])[0];
                if (node.type == 1) {
                    if (!component.disabled) component.checked = false;
                    else this.dispatchEvent("treeToggleChange");
                }
                
                else component.reset();
            }
        },
        appendOption: function (option, bk) {
            option.nodes = option.nodes || [];
            option.type = option.type || 1;
            this.insertAdjacentHTML(this.domList.strTree, this.data.liHTML, "beforeend", function () {
                this.data.nodes.push(option);
                if (typeof bk === "function") bk();
            }.bind(this), { node: option });
        },
        insertOption: function (option, value, bk) {
            option.nodes = option.nodes || [];
            option.type = option.type || 1;
            if (!value) return this.appendOption(option, bk);
            else {
                var nodes = this.data.nodes, childs = this.domList.strTree.children;
                for (var i = 0; i < nodes.length; i++) {
                    if (nodes[i].value == value) {
                        var child = childs[i];
                        this.insertAdjacentHTML(child, this.data.liHTML, "beforebegin", function () {
                            nodes.splice(i, 0, option);
                            if (typeof bk === "function") bk();
                        }.bind(this), { node: option });
                        return;
                    }
                }
            }
        },
        removeOption: function (value, bk) {
            var nodes = this.data.nodes, childs = this.domList.strTree.children;
            for (var i = 0; i < nodes.length; i++) {
                if (nodes[i].value == value) {
                    var child = childs[i];
                    this.innerHTML(child, "", function () {
                        nodes.splice(i, 1);
                        if (typeof bk === "function") bk();
                    }.bind(this));
                    return;
                }
            }
        },
        getTreeByValue: function (value) {
            var nodes = this.data.nodes, childs = this.domList.strTree.children;
            for (var i = 0; i < nodes.length; i++) {
                var node = nodes[i];
                if (node.type == 2) {
                    var tree = this.getComponentsByInDom(childs[i])[0];
                    if (node.value == value) return tree;
                    var result = tree.getTreeByValue(value);
                    if (result) return result;
                } 
            }
        },
        _getTreeObjByValue: function (value) {
            var nodes = this.data.nodes, childs = this.domList.strTree.children;
            for (var i = 0; i < nodes.length; i++) {
                var node = nodes[i], component = this.getComponentsByInDom(childs[i])[0];
                if (node.type == 1) {
                    if (node.value == value) return {
                        component: component,
                        target: this
                    }
                }
                else {
                    var obj = component._getTreeObjByValue(value);
                    if (obj) return obj;
                }
            }
            return false;
        },
        init: function () {
            if (this.domList.treeToggle) {
                var state = this.getState();
                if (state == "indeterminate") this.domList.treeToggle.indeterminate = true;
            }
        },
        getState: function () {
            var childs = this.domList.strTree.children, allChecked = true, allNotChecked = true, isInter = false;
            for (var i = 0; i < childs.length; i++) {
                var components = this.getComponentsByInDom(childs[i]);
                var checked = components[0].checked;
                isInter = isInter || components[0].indeterminate;
                allChecked = allChecked && checked;
                allNotChecked = allNotChecked && !checked;
            }
            if ((!allChecked && !allNotChecked) || (allNotChecked && isInter)) return "indeterminate"
            if (allChecked) return "checked";
            return "notchecked";
        },
        getDomObj: function () {
            this.attachDom(".str-tree", "strTree")
                .attachDiyEvent("treeToggleChange", this.treeToggleHandler)
                .attachEvent("strTree", "togglechange", this.toggleChangeHandler, false);
            
            if (this.property.haschecked) {
                this.attachDom("#treeToggle", "treeToggle")
                    .attachDom("#expand", "expand")
                    .attachEvent("expand", "iconclick", this.expandHandler, false);
            }
        },
        expandHandler: function (ev) {
            var component = ev.detail.component;
            switch (component.icon) {
                case "menu-right": component.icon = "menu-down"; break;
                case "menu-down": component.icon = "menu-right"; break;
            }
        },
        treeToggleHandler: function (ev) {
            var treeToggle = this.domList.treeToggle;
            if (treeToggle) {
                treeToggle.indeterminate = false;
                var state = this.getState();
                switch (state) {
                    case "indeterminate": treeToggle.indeterminate = true; break;
                    case "checked": treeToggle.checked = true; break;
                    case "notchecked": treeToggle.checked = false; break;
                }
            }
            this.parent.dispatchEvent("treeToggleChange");
        },
        _changeValue: function (component) {
            if (component.dataset.type == "head") {
                component.parent.checked = component.checked;
            }
            else {
                if (this.domList.treeToggle) {
                    this.domList.treeToggle.indeterminate = false;
                    var state = this.getState();
                    switch (state) {
                        case "indeterminate": this.domList.treeToggle.indeterminate = true; break;
                        case "checked": this.domList.treeToggle.checked = true; break;
                        case "notchecked": this.domList.treeToggle.checked = false; break;
                    }
                }
            }
            this.dispatchEvent("treeToggleChange");
        },
        toggleChangeHandler: function (ev) {
            if (ev) ev.stopPropagation();
            var component = ev.detail.component;
            this._changeValue(component);
        },
        computedData: function () {
            var property = this.property;
            this.data.nodes = htmlTool.getOptionAndOptGroup(this.slot);
            property.haschecked = typeof property.haschecked === "undefined" ? false : true;
            property.checked = typeof property.checked === "undefined" ? false : true;
        },
        filter: {
            filterValue: function (obj, isText) {
                if (isText === "true") return true;
                return obj.checked;
            }
        }
    })
})